﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JOATServer.SocietyFramework.Core.XML
{
    public abstract partial class BaseXMLAutomaton : BaseAutomaton
    {
        /// <summary>
        /// Class which represents a variable value. If it is a direct instruction in an instruction block then it's considered
        /// a return value for the block. If it's part of an expression inside an instruction then the instruction is considered
        /// a normal variable value.
        /// </summary>
        protected class VariableInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public string name;

            public VariableInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public virtual object Code()
            {
                return Owner.variables[name];
            }
        }

        /// <summary>
        /// Class which represents a variable with index values. If it is a direct instruction in an instruction block then
        /// it's considered a return value for the block. If it's part of an expression inside an instruction then the 
        /// instruction is considered a normal variable value.
        /// </summary>
        protected class VariableWithIndexInstruction : VariableInstruction
        {
            public List<Instruction> indexValues;

            public VariableWithIndexInstruction(BaseXMLAutomaton owner)
                : base(owner)
            {
                this.indexValues = new List<BaseXMLAutomaton.Instruction>();
            }

            public override object Code()
            {
                Dictionary<object, object> currentVariable = (Dictionary<object, object>)Owner.variables[name];
                for (int i = 0; i < indexValues.Count - 1; i++)
                {
                    currentVariable = (Dictionary<object, object>)currentVariable[indexValues[i].Code()];
                }
                return currentVariable[indexValues[indexValues.Count - 1].Code()];
            }

        }

        /* ******************** OPERATION INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// A class used to identify that an instruction has one operand to complete in the expression construction process.
        /// </summary>
        protected abstract class OneOperandInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction operand;

            public OneOperandInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public virtual object Code()
            {
                return null;
            }
        }

        /// <summary>
        /// A class used to identify that an instruction has two operands to complete in the expression construction process.
        /// </summary>
        protected abstract class TwoOperandsInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction leftOperand, rightOperand;

            public TwoOperandsInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public virtual object Code()
            {
                return null;
            }
        }

        /// <summary>
        /// Class which represents an assign instruction. The leftOperand is an instruction containing the name of the 
        /// variable to be assigned to. It can be both a VariableInstruction or a VariableWithIndexInstruction. In case
        /// it's a VariableWithIndexInstruction the maps are created automatically. If the variable name doesn't exist 
        /// it is created automatically. The rightOperand is an instruction which must return the assigned value. The
        /// returned value is the leftOpernad.
        /// </summary>
        protected class AssignInstruction : TwoOperandsInstruction
        {
            public AssignInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                if (leftOperand is VariableWithIndexInstruction)
                {
                    VariableWithIndexInstruction firstVariable = ((VariableWithIndexInstruction)leftOperand);
                    object currentVariable = Owner.variables.Get(firstVariable.name);
                    if (currentVariable == null || !(currentVariable is Dictionary<object, object>))
                    {
                        currentVariable = new Dictionary<object, object>();
                        Owner.variables.Add(firstVariable.name, currentVariable);
                    }

                    object currentIndex = null;
                    Dictionary<object, object> currentDictionary = (Dictionary<object, object>)currentVariable;

                    for (int i = 0; i < firstVariable.indexValues.Count - 1; i++)
                    {
                        currentIndex = firstVariable.indexValues[i].Code();
                        currentVariable = currentDictionary.Get(currentIndex);
                        if (currentVariable == null || !(currentVariable is Dictionary<object, object>))
                        {
                            currentVariable = new Dictionary<object, object>();
                            currentDictionary.Add(currentIndex, currentVariable);
                        }
                        currentDictionary = (Dictionary<object, object>)currentVariable;
                    }
                    currentIndex = firstVariable.indexValues[firstVariable.indexValues.Count - 1].Code();
                    currentDictionary.Add(currentIndex, rightOperand.Code());
                }
                else
                {
                    string name = ((VariableInstruction)leftOperand).name;
                    object value = rightOperand.Code();
                    if(Owner.variables.ContainsKey(name))
                    {
                        Owner.variables.Remove(name);
                    }
                    Owner.variables.Add(name, value);
                }
                return leftOperand;
            }
        }

        /// <summary>
        /// Class which represents the addition(+) operator. The left operand and the right operand can be one of the following
        /// types: Int32, Double or String. When the left or right operand is String, the other can be Boolean.
        /// </summary>
        protected class AdditionInstruction : TwoOperandsInstruction
        {
            public AdditionInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left + (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left + (Double)right;
                    }
                    else if (right is String)
                    {
                        return (Int32)left + (String)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left + (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left + (Double)right;
                    }
                    else if (right is String)
                    {
                        return (Double)left + (String)right;
                    }
                }
                else if (left is String)
                {
                    if (right is Int32)
                    {
                        return (String)left + (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (String)left + (Double)right;
                    }
                    else if (right is String)
                    {
                        return (String)left + (String)right;
                    }
                    else if (right is Boolean)
                    {
                        return (String)left + (Boolean)right;
                    }
                }
                else if (left is Boolean && right is String)
                {
                    return (Boolean)left + (String)right;
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the subtraction(-) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class SubtractionInstruction : TwoOperandsInstruction
        {
            public SubtractionInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left - (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left - (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left - (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left - (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the multiplication(*) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class MultiplicationInstruction : TwoOperandsInstruction
        {
            public MultiplicationInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left * (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left * (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left * (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left * (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the division(/) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class DivisionInstruction : TwoOperandsInstruction
        {
            public DivisionInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left / (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left / (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left / (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left / (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the modulo(%) operator. The left operand and the right operand can be one of the following
        /// types: Int32.
        /// </summary>
        protected class ModuloInstruction : TwoOperandsInstruction
        {
            public ModuloInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return (Int32)leftOperand.Code() % (Int32)rightOperand.Code();
            }

        }

        /* ******************** BOOLEAN OPERATION INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// Class which represents the less(&lt;) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class LessInstruction : TwoOperandsInstruction
        {
            public LessInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left < (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left < (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left < (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left < (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the less or equal(&lt;=) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class LessOrEqualInstruction : TwoOperandsInstruction
        {
            public LessOrEqualInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left <= (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left <= (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left <= (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left <= (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the greater(&gt;) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class GreaterInstruction : TwoOperandsInstruction
        {
            public GreaterInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left > (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left > (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left > (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left > (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the greater or equal(>=) operator. The left operand and the right operand can be one of the following
        /// types: Int32 and Double.
        /// </summary>
        protected class GreaterOrEqualInstruction : TwoOperandsInstruction
        {
            public GreaterOrEqualInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                object left = leftOperand.Code();
                object right = rightOperand.Code();
                if (left is Int32)
                {
                    if (right is Int32)
                    {
                        return (Int32)left >= (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Int32)left >= (Double)right;
                    }
                }
                else if (left is Double)
                {
                    if (right is Int32)
                    {
                        return (Double)left >= (Int32)right;
                    }
                    else if (right is Double)
                    {
                        return (Double)left >= (Double)right;
                    }
                }
                return null;
            }

        }

        /// <summary>
        /// Class which represents the equality(==) operator. The left operand and the right operand can be one of the following
        /// types: Boolean, Int32, Double and String.
        /// </summary>
        protected class EqualsInstruction : TwoOperandsInstruction
        {
            public EqualsInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return leftOperand.Code().Equals(rightOperand.Code());
            }

        }

        /// <summary>
        /// Class which represents the non-equality(!=) operator. The left operand and the right operand can be one of the following
        /// types: Boolean, Int32, Double and String.
        /// </summary>
        protected class NotEqualsInstruction : TwoOperandsInstruction
        {
            public NotEqualsInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return !leftOperand.Code().Equals(rightOperand.Code());
            }

        }

        /// <summary>
        /// Class which represents the and(&&) operator. The left operand and the right operand can be one of the following
        /// types: Boolean.
        /// </summary>
        protected class AndInstruction : TwoOperandsInstruction
        {
            public AndInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return (Boolean)leftOperand.Code() && (Boolean)rightOperand.Code();
            }

        }

        /// <summary>
        /// Class which represents the or(||) operator. The left operand and the right operand can be one of the following
        /// types: Boolean.
        /// </summary>
        protected class OrInstruction : TwoOperandsInstruction
        {
            public OrInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return (Boolean)leftOperand.Code() || (Boolean)rightOperand.Code();
            }

        }

        /// <summary>
        /// Class which represents the not(!) operator. The argument can be one of the following types: Boolean.
        /// </summary>
        protected class NotInstruction : OneOperandInstruction
        {
            public NotInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return !(Boolean)operand.Code();
            }

        }
    }
}
